package pl.mchaniewski.ryneczek.scheduler.task;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import pl.mchaniewski.ryneczek.dao.OrderDao;
import pl.mchaniewski.ryneczek.dao.PortfolioDao;
import pl.mchaniewski.ryneczek.dao.QuotationDao;
import pl.mchaniewski.ryneczek.pojo.Order;
import pl.mchaniewski.ryneczek.pojo.Portfolio;
import pl.mchaniewski.ryneczek.pojo.Quotation;
import pl.mchaniewski.ryneczek.utilities.DateUtil;

@Component("orderRefresher")
public class OrderRefresher {
    private static final Logger logger = LoggerFactory
            .getLogger(OrderRefresher.class);
    private OrderDao orderDao;
    private QuotationDao quotationDao;
    private PortfolioDao portfolioDao;

    @Autowired
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    @Autowired
    public void setQuotationDao(QuotationDao quotationDao) {
        this.quotationDao = quotationDao;
    }

    @Autowired
    public void setPortfolioDao(PortfolioDao portfolioDao) {
        this.portfolioDao = portfolioDao;
    }

    public void run() throws Exception {
        List<Order> orderList = null;

        Date now = DateUtil.getNow();

        try {
            orderList = orderDao.findAllWithStatusBeforeDate(
                    Order.STATUS_ACCEPTED, now, true);

            if (logger.isDebugEnabled()) {
                logger.debug("Grabbing orders before {} date. Now: {}", now,
                        now);
            }
        } catch (Exception ex) {
            logger.warn(
                    "Couldn't grab order list. Exception: {} Stack trace: {}",
                    ex.getMessage(), ex.getStackTrace());
        }

        if (orderList == null || orderList.size() == 0) {
            logger.info("Order list was empty. No orders.");

            return;
        } else {
            for (Order order : orderList) {
                Date expiryDate = order.getExpiryDate();
                if (DateUtil.isDateBeforeNow(expiryDate)) {
                    order.setStatus(Order.STATUS_CANCELED);

                    if (logger.isDebugEnabled()) {
                        Object[] args = { order.getOrderid(),
                                DateUtil.getCurrentTime(), expiryDate };
                        logger.debug(
                                "Order {} cancelled because of expiry date. Now: {} Limit: {}",
                                args);
                    }

                    orderDao.save(order);
                    continue;
                }

                List<Quotation> latestQuotations = null;
                try {
                    latestQuotations = quotationDao.findLatest(order
                            .getCompany().getCompanyid(), true);
                } catch (Exception ex) {
                    logger.warn(
                            "Couldn't fetch lastest quotation based on CompanyID. Exception: {} Stack trace: {}",
                            ex.getMessage(), ex.getStackTrace());

                    throw ex;
                }

                Quotation latestQuotation = null;
                if (latestQuotations == null || latestQuotations.size() == 0) {
                    logger.info("Company wasn't in last update.");

                    continue;
                } else {
                    latestQuotation = latestQuotations.get(0);
                }

                if (order.getPriceType() != Order.PRICE_TYPE_PKC) {
                    if (order.getTransactionType().equals(
                            Order.TRANSACTION_TYPE_BUY)
                            && order.getSetPrice().compareTo(
                                    latestQuotation.getCurrentValue()) == 1) {
                        if (logger.isDebugEnabled()) {
                            Object[] args = { order.getOrderid(),
                                    latestQuotation.getCurrentValue(),
                                    order.getSetPrice() };
                            logger.debug(
                                    "Buy order {} doesn't meet requirements. Now: {} Limit: {}",
                                    args);
                        }

                        continue;
                    }

                    if (order.getTransactionType().equals(
                            Order.TRANSACTION_TYPE_SELL)
                            && order.getSetPrice().compareTo(
                                    latestQuotation.getCurrentValue()) == -1) {
                        if (logger.isDebugEnabled()) {
                            Object[] args = { order.getOrderid(),
                                    latestQuotation.getCurrentValue(),
                                    order.getSetPrice() };
                            logger.debug(
                                    "Sell order {} doesn't meet requirements. Now: {} Limit: {}",
                                    args);
                        }

                        continue;
                    }
                }

                if (order.getPriceType() == Order.PRICE_TYPE_STOP_LOSS) {
                    if (logger.isDebugEnabled()) {
                        Object[] args = { order.getOrderid(),
                                latestQuotation.getCurrentValue(),
                                order.getSetPrice() };
                        logger.debug(
                                "Stop loss order {} changed into PKC. Now: {} Limit: {}",
                                args);
                    }

                    order.setPriceType(Order.PRICE_TYPE_PKC);

                    orderDao.save(order);
                    continue;
                }

                order.setOriginalPrice(latestQuotation.getCurrentValue());

                Portfolio portf = order.getPortfolio();
                BigDecimal balance = portf.getBalance();
                BigDecimal totalCommissions = portf.getTotalCommissions();

                BigDecimal transactionVal = Order.getTransactionValue(
                        order.getAmount(), order.getOriginalPrice());
                BigDecimal commissionVal = Order
                        .getCommissionValue(transactionVal);

                BigDecimal totalBuyValue = transactionVal.add(commissionVal);
                BigDecimal totalSellValue = transactionVal
                        .subtract(commissionVal);

                if (order.getTransactionType().equals(
                        Order.TRANSACTION_TYPE_BUY)) {
                    portf.setBalance(balance.subtract(totalBuyValue));
                }

                if (order.getTransactionType().equals(
                        Order.TRANSACTION_TYPE_SELL)) {
                    portf.setBalance(balance.add(totalSellValue));
                }

                portf.setTotalCommissions(totalCommissions.add(commissionVal));
                order.setStatus(Order.STATUS_COMPLETED);
                order.setExpiryDate(DateUtil.getNow());

                try {
                    orderDao.save(order);
                    portfolioDao.save(portf);

                    if (logger.isDebugEnabled()) {
                        Object[] args = { order.getOrderid(),
                                latestQuotation.getCurrentValue(),
                                order.getSetPrice() };
                        logger.debug("Order {} completed. Now: {} Limit: {}",
                                args);
                    }
                } catch (Exception ex) {
                    logger.warn(
                            "Couldn't save order and portfolio. Exception: {} Stack trace: {}",
                            ex.getMessage(), ex.getStackTrace());

                    throw ex;
                }
            }
        }
    }
}
